﻿using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using SharePointUtilitiesForDevelopers.Mapper;
using SharePointUtilitiesForDevelopers.Service;
using SharePointUtilitiesForDevelopers.ListsService;

namespace SharePointUtilitiesForDevelopers.Gateway
{
    /// <summary>
    /// Provides a gateway to the SharePoint lists service.
    /// </summary>
    public class SharePointListsServiceGateway : ISharePointListsServiceGateway
    {

        /// <summary>
        /// Deletes list items in the specified list / view.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="viewName">Name of the view.</param>
        /// <param name="query">The query xml.</param>
        /// <param name="batchSize">Size of the batch.</param>
        public void DeleteListItems(
            string siteUrl, 
            string listName, 
            string viewName,
            XmlElement query,
            int batchSize)
        {

            if (query == null)
            {
                query = SharePointXml.ParseXml(null, "Query");
            }

            while (true)
            {
                // Get a batch of items to delete
                var items = GetListItems(
                    siteUrl,
                    listName,
                    viewName,
                    query,
                    SharePointXml.ParseXml(null, "ViewFields"),
                    batchSize.ToString(),
                    SharePointXml.ParseXml(null, "QueryOptions"),
                    null,
                    new SharePointListItemMapper());

                // Delete the items
                DeleteListItems(siteUrl, listName, items);

                //TODO: inspect for errors to prevent infinite loop
                
                // If there are no more items to delete, exit
                if(items.Count() < batchSize)
                {
                    break;
                }
            }

        }

        /// <summary>
        /// Deletes the list items.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="items">The items.</param>
        private void DeleteListItems(
            string siteUrl, 
            string listName, 
            IEnumerable<SharePointListItem> items)
        {
            // Get the list
            var list = GetList(siteUrl, listName, new SharePointListMapper());

            // Get the xml to batch delete the items
            var batchDeletes = items.ToBatchDeleteXElement(list.Version).ToXmlElement();

            // Get the proxy
            var client = SharePointServiceClientFactory.CreateInstance<ListsSoapClient, ListsSoap>(siteUrl);

            // Delete the items
            client.UpdateListItems(listName, batchDeletes);

        }

        /// <summary>
        /// Gets the list items.
        /// </summary>
        /// <typeparam name="TDomainObject">The type of the domain object.</typeparam>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name or Guid of the list.</param>
        /// <param name="viewName">Guid of the view.</param>
        /// <param name="query">The query.</param>
        /// <param name="viewFields">The view fields.</param>
        /// <param name="rowLimit">The row limit.</param>
        /// <param name="queryOptions">The query options.</param>
        /// <param name="webId">The web id.</param>
        /// <param name="mapper">The mapper.</param>
        /// <returns>A list of <see cref="TDomainObject"/> objects.</returns>
        public IEnumerable<TDomainObject> GetListItems<TDomainObject>(
            string siteUrl, 
            string listName, 
            string viewName, 
            XmlElement query, 
            XmlElement viewFields, 
            string rowLimit, 
            XmlElement queryOptions, 
            string webId, 
            IMapper<XElement, TDomainObject> mapper)
        {
            if (query == null)
            {
                query = SharePointXml.ParseXml(null, "Query");
            }

            if (viewFields == null)
            {
                viewFields = SharePointXml.ParseXml(null, "ViewFields");
            }

            if (queryOptions == null)
            {
                queryOptions = SharePointXml.ParseXml(null, "QueryOptions");
            }

            // Get the proxy
            var client = SharePointServiceClientFactory.CreateInstance<ListsSoapClient, ListsSoap>(siteUrl);
            
            // Get the response
            var response =
                XElement.Parse(client.GetListItems(listName, viewName, query, viewFields, rowLimit, queryOptions, webId).OuterXml);

            // Return the mapped object
            return mapper.MapList(response);

        }

        /// <summary>
        /// Gets a list content type.
        /// </summary>
        /// <typeparam name="TDomainObject">The type of the domain object.</typeparam>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="contentTypeId">The content type id.</param>
        /// <param name="mapper">The mapper.</param>
        /// <returns>A <see cref="TDomainObject"/>.</returns>
        public TDomainObject GetListContentType<TDomainObject>(
            string siteUrl, 
            string listName, 
            string contentTypeId,
            IMapper<XElement, TDomainObject> mapper)
        {
            // Get the proxy
            var client = SharePointServiceClientFactory.CreateInstance<ListsSoapClient, ListsSoap>(siteUrl);
            
            // Get the response
            var response = XElement.Parse(client.GetListContentType(listName, contentTypeId).OuterXml);
            
            // Return the mapped object
            return mapper.Map(response);
            
        }

        /// <summary>
        /// Gets the specified list.
        /// </summary>
        /// <typeparam name="TDomainObject">The type of the domain object.</typeparam>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">The list name.</param>
        /// <param name="mapper">The mapper.</param>
        /// <returns>A <see cref="TDomainObject"/>.</returns>
        public TDomainObject GetList<TDomainObject>(
            string siteUrl, 
            string listName,
            IMapper<XElement, TDomainObject> mapper)
        {
            // Get the proxy
            var client = SharePointServiceClientFactory.CreateInstance<ListsSoapClient, ListsSoap>(siteUrl);

            // Get the response
            var response = XElement.Parse(client.GetList(listName).OuterXml);

            // Return the mapped object
            return mapper.Map(response);

        }

        /// <summary>
        /// Gets the lists in the specified web.
        /// </summary>
        /// <typeparam name="TDomainObject">The type of the domain object.</typeparam>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="mapper">The mapper.</param>
        /// <returns>A list of <see cref="TDomainObject"/> objects.</returns>
        public IEnumerable<TDomainObject> GetLists<TDomainObject>(
            string siteUrl, 
            IMapper<XElement, TDomainObject> mapper)
        {
            // Get the proxy
            var client = SharePointServiceClientFactory.CreateInstance<ListsSoapClient, ListsSoap>(siteUrl);
            
            // Get the response
            var response = XElement.Parse(client.GetListCollection().OuterXml);

            // Return the mapped object
            return mapper.MapList(response);

        }

        /// <summary>
        /// Gets the fields in the specified list.
        /// </summary>
        /// <typeparam name="TDomainObject">The type of the domain object.</typeparam>
        /// <param name="siteUrl">The site url.</param>
        /// <param name="listName">The list name.</param>
        /// <param name="mapper"></param>
        /// <returns>A list of <see cref="TDomainObject"/> objects.</returns>
        public IEnumerable<TDomainObject> GetListFields<TDomainObject>(
            string siteUrl, 
            string listName,
            IMapper<XElement, TDomainObject> mapper)
        {
            // Get the proxy
            var client = SharePointServiceClientFactory.CreateInstance<ListsSoapClient, ListsSoap>(siteUrl);
            
            // Get the response
            var response = XElement.Parse(client.GetList(listName).OuterXml);

            // Return the mapped object
            return mapper.MapList(response);

        }

        /// <summary>
        /// Gets the content types.
        /// </summary>
        /// <typeparam name="TDomainObject">The type of the domain object.</typeparam>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="mapper">The mapper.</param>
        /// <returns>A list of <see cref="TDomainObject"/> objects.</returns>
        public IEnumerable<TDomainObject> GetListContentTypes<TDomainObject>(
            string siteUrl, 
            string listName,
            IMapper<XElement, TDomainObject> mapper)
        {
            // Get the proxy
            var client = SharePointServiceClientFactory.CreateInstance<ListsSoapClient, ListsSoap>(siteUrl);

            // Get the reponse
            var response = XElement.Parse(client.GetListContentTypes(listName, string.Empty).OuterXml);

            // Return the mapped object
            return mapper.MapList(response);
        }
    }
}